home *** CD-ROM | disk | FTP | other *** search
- /*
- ** genmap - Generate a colormap with the specified number of
- ** entries.
- **
- ** Jack Jansen, CWI, 1995.
- **
- ** Modified from ppmquant.c, which is
- **
- ** Copyright (C) 1989, 1991 by Jef Poskanzer.
- **
- ** Permission to use, copy, modify, and distribute this software and its
- ** documentation for any purpose and without fee is hereby granted, provided
- ** that the above copyright notice appear in all copies and that both that
- ** copyright notice and this permission notice appear in supporting
- ** documentation. This software is provided "as is" without express or
- ** implied warranty.
- */
-
- #include "Python.h"
- #include "mppmcmap.h"
-
- #define MAXCOLORS 32767
-
- /* #define LARGE_NORM */
- #define LARGE_LUM
-
- /* #define REP_CENTER_BOX */
- /* #define REP_AVERAGE_COLORS */
- #define REP_AVERAGE_PIXELS
-
- typedef struct box* box_vector;
- struct box
- {
- int ind;
- int colors;
- int sum;
- };
-
- typedef int (*qsfuncptr)Py_PROTO((const void *, const void *));
-
- static int mediancut Py_PROTO((colorhist_vector chv, int colors, int sum,
- int newcolors, pixel *colormap));
- static int redcompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
- static int greencompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
- static int bluecompare Py_PROTO((colorhist_vector ch1, colorhist_vector ch2));
- static int sumcompare Py_PROTO((box_vector b1, box_vector b2));
-
- int
- mppm_genmap(pixels, cols, rows, mapdata, newcolors)
- long *pixels;
- int cols, rows;
- long *mapdata;
- int newcolors;
- {
- #if 0
- int argc;
- char* argv[];
- {
- FILE* ifp;
- pixel** pixels;
- pixel** mappixels;
- register pixel* pP;
- int argn, rows, cols, maprows, mapcols, row;
- register int col, limitcol;
- pixval maxval, newmaxval, mapmaxval;
- int newcolors, colors;
- register int ind;
- colorhash_table cht;
- int floyd;
- int usehash;
- long* thisrerr;
- long* nextrerr;
- long* thisgerr;
- long* nextgerr;
- long* thisberr;
- long* nextberr;
- long* temperr;
- register long sr, sg, sb, err;
- #define FS_SCALE 1024
- int fs_direction;
- char* usage = "[-floyd|-fs] <ncolors> [ppmfile]\n [-floyd|-fs] -map mapfile [ppmfile]";
- #endif
- colorhist_vector chv;
- int colors, rv;
-
- /*
- ** Step 2: attempt to make a histogram of the colors, unclustered.
- */
- chv = mppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors );
- if ( chv == (colorhist_vector) 0 ) {
- return 0;
- #if 0
- XXXX this code should go to a separate 'scale' routine.
- pm_message( "too many colors!" );
- newmaxval = maxval / 2;
- pm_message(
- "scaling colors from maxval=%d to maxval=%d to improve clustering...",
- maxval, newmaxval );
- for ( row = 0; row < rows; ++row )
- for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP )
- MPPM_DEPTH( *pP, *pP, maxval, newmaxval );
- maxval = newmaxval;
- #endif
- }
- /*
- ** Step 3: apply median-cut to histogram, making the new colormap.
- */
- rv = mediancut( chv, colors, rows * cols, newcolors, mapdata );
- mppm_freecolorhist( chv );
- return rv;
- }
-
- /*
- ** Here is the fun part, the median-cut colormap generator. This is based
- ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
- ** Display", SIGGRAPH '82 Proceedings, page 297.
- */
-
- #if __STDC__
- static int
- mediancut( colorhist_vector chv, int colors, int sum,
- int newcolors, pixel *colormap )
- #else /*__STDC__*/
- static int
- mediancut( chv, colors, sum, newcolors, colormap )
- colorhist_vector chv;
- int colors, sum, newcolors;
- pixel *colormap;
- #endif /*__STDC__*/
- {
- box_vector bv;
- register int bi, i;
- int boxes;
-
- bv = (box_vector) malloc( sizeof(struct box) * newcolors );
- if ( bv == (box_vector) 0 ) {
- return -1;
- }
-
- /*
- ** Set up the initial box.
- */
- bv[0].ind = 0;
- bv[0].colors = colors;
- bv[0].sum = sum;
- boxes = 1;
-
- /*
- ** Main loop: split boxes until we have enough.
- */
- while ( boxes < newcolors )
- {
- register int indx, clrs;
- int sm;
- register int minr, maxr, ming, maxg, minb, maxb, v;
- int halfsum, lowersum;
-
- /*
- ** Find the first splittable box.
- */
- for ( bi = 0; bi < boxes; ++bi )
- if ( bv[bi].colors >= 2 )
- break;
- if ( bi == boxes )
- break; /* ran out of colors! */
- indx = bv[bi].ind;
- clrs = bv[bi].colors;
- sm = bv[bi].sum;
-
- /*
- ** Go through the box finding the minimum and maximum of each
- ** component - the boundaries of the box.
- */
- minr = maxr = MPPM_GETR( chv[indx].color );
- ming = maxg = MPPM_GETG( chv[indx].color );
- minb = maxb = MPPM_GETB( chv[indx].color );
- for ( i = 1; i < clrs; ++i )
- {
- v = MPPM_GETR( chv[indx + i].color );
- if ( v < minr ) minr = v;
- if ( v > maxr ) maxr = v;
- v = MPPM_GETG( chv[indx + i].color );
- if ( v < ming ) ming = v;
- if ( v > maxg ) maxg = v;
- v = MPPM_GETB( chv[indx + i].color );
- if ( v < minb ) minb = v;
- if ( v > maxb ) maxb = v;
- }
-
- /*
- ** Find the largest dimension, and sort by that component. I have
- ** included two methods for determining the "largest" dimension;
- ** first by simply comparing the range in RGB space, and second
- ** by transforming into luminosities before the comparison. You
- ** can switch which method is used by switching the commenting on
- ** the LARGE_ defines at the beginning of this source file.
- */
- #ifdef LARGE_NORM
- if ( maxr - minr >= maxg - ming && maxr - minr >= maxb - minb )
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)redcompare );
- else if ( maxg - ming >= maxb - minb )
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)greencompare );
- else
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)bluecompare );
- #endif /*LARGE_NORM*/
- #ifdef LARGE_LUM
- {
- pixel p;
- float rl, gl, bl;
-
- MPPM_ASSIGN(p, maxr - minr, 0, 0);
- rl = MPPM_LUMIN(p);
- MPPM_ASSIGN(p, 0, maxg - ming, 0);
- gl = MPPM_LUMIN(p);
- MPPM_ASSIGN(p, 0, 0, maxb - minb);
- bl = MPPM_LUMIN(p);
-
- if ( rl >= gl && rl >= bl )
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)redcompare );
- else if ( gl >= bl )
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)greencompare );
- else
- qsort(
- (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- (qsfuncptr)bluecompare );
- }
- #endif /*LARGE_LUM*/
-
- /*
- ** Now find the median based on the counts, so that about half the
- ** pixels (not colors, pixels) are in each subdivision.
- */
- lowersum = chv[indx].value;
- halfsum = sm / 2;
- for ( i = 1; i < clrs - 1; ++i )
- {
- if ( lowersum >= halfsum )
- break;
- lowersum += chv[indx + i].value;
- }
-
- /*
- ** Split the box, and sort to bring the biggest boxes to the top.
- */
- bv[bi].colors = i;
- bv[bi].sum = lowersum;
- bv[boxes].ind = indx + i;
- bv[boxes].colors = clrs - i;
- bv[boxes].sum = sm - lowersum;
- ++boxes;
- qsort( (char*) bv, boxes, sizeof(struct box), (qsfuncptr)sumcompare );
- }
-
- /*
- ** Ok, we've got enough boxes. Now choose a representative color for
- ** each box. There are a number of possible ways to make this choice.
- ** One would be to choose the center of the box; this ignores any structure
- ** within the boxes. Another method would be to average all the colors in
- ** the box - this is the method specified in Heckbert's paper. A third
- ** method is to average all the pixels in the box. You can switch which
- ** method is used by switching the commenting on the REP_ defines at
- ** the beginning of this source file.
- */
- for ( bi = 0; bi < boxes; ++bi )
- {
- #ifdef REP_CENTER_BOX
- register int indx = bv[bi].ind;
- register int clrs = bv[bi].colors;
- register int minr, maxr, ming, maxg, minb, maxb, v;
-
- minr = maxr = MPPM_GETR( chv[indx].color );
- ming = maxg = MPPM_GETG( chv[indx].color );
- minb = maxb = MPPM_GETB( chv[indx].color );
- for ( i = 1; i < clrs; ++i )
- {
- v = MPPM_GETR( chv[indx + i].color );
- minr = min( minr, v );
- maxr = max( maxr, v );
- v = MPPM_GETG( chv[indx + i].color );
- ming = min( ming, v );
- maxg = max( maxg, v );
- v = MPPM_GETB( chv[indx + i].color );
- minb = min( minb, v );
- maxb = max( maxb, v );
- }
- MPPM_ASSIGN(
- colormap[bi], ( minr + maxr ) / 2, ( ming + maxg ) / 2,
- ( minb + maxb ) / 2 );
- #endif /*REP_CENTER_BOX*/
- #ifdef REP_AVERAGE_COLORS
- register int indx = bv[bi].ind;
- register int clrs = bv[bi].colors;
- register long r = 0, g = 0, b = 0;
-
- for ( i = 0; i < clrs; ++i )
- {
- r += MPPM_GETR( chv[indx + i].color );
- g += MPPM_GETG( chv[indx + i].color );
- b += MPPM_GETB( chv[indx + i].color );
- }
- r = r / clrs;
- g = g / clrs;
- b = b / clrs;
- MPPM_ASSIGN( colormap[bi], r, g, b );
- #endif /*REP_AVERAGE_COLORS*/
- #ifdef REP_AVERAGE_PIXELS
- register int indx = bv[bi].ind;
- register int clrs = bv[bi].colors;
- register long r = 0, g = 0, b = 0, sum = 0;
-
- for ( i = 0; i < clrs; ++i )
- {
- r += MPPM_GETR( chv[indx + i].color ) * chv[indx + i].value;
- g += MPPM_GETG( chv[indx + i].color ) * chv[indx + i].value;
- b += MPPM_GETB( chv[indx + i].color ) * chv[indx + i].value;
- sum += chv[indx + i].value;
- }
- r = r / sum;
- if ( r > MAXVAL ) r = MAXVAL; /* avoid math errors */
- g = g / sum;
- if ( g > MAXVAL ) g = MAXVAL;
- b = b / sum;
- if ( b > MAXVAL ) b = MAXVAL;
- MPPM_ASSIGN( colormap[bi], r, g, b );
- #endif /*REP_AVERAGE_PIXELS*/
- }
-
- /*
- ** All done.
- */
- return newcolors;
- }
-
- static int
- redcompare( ch1, ch2 )
- colorhist_vector ch1, ch2;
- {
- return (int) MPPM_GETR( ch1->color ) - (int) MPPM_GETR( ch2->color );
- }
-
- static int
- greencompare( ch1, ch2 )
- colorhist_vector ch1, ch2;
- {
- return (int) MPPM_GETG( ch1->color ) - (int) MPPM_GETG( ch2->color );
- }
-
- static int
- bluecompare( ch1, ch2 )
- colorhist_vector ch1, ch2;
- {
- return (int) MPPM_GETB( ch1->color ) - (int) MPPM_GETB( ch2->color );
- }
-
- static int
- sumcompare( b1, b2 )
- box_vector b1, b2;
- {
- return b2->sum - b1->sum;
- }
-